import View from './components/view'
import Link from './components/link'

// 保存初始化传递进来的Vue实例
export let _Vue

export function install (Vue) {
  // 通过一个全局变量来确保只安装一次
  if (install.installed && _Vue === Vue) return
  install.installed = true

  _Vue = Vue

  const isDef = v => v !== undefined

  // 注册实例
  const registerInstance = (vm, callVal) => {
    // 至少存在一个 VueComponent 时, _parentVnode 属性才存在
    let i = vm.$options._parentVnode
    // registerRouteInstance 在 src/components/view.js
    if (isDef(i) && isDef(i = i.data) && isDef(i = i.registerRouteInstance)) {
      // 这里的 i 就是 registerRouteInstance 方法，调用的就是这个方法
      i(vm, callVal)
    }
  }

  // 全局混入-官方文档：https://cn.vuejs.org/v2/guide/mixins.html#全局混入
  // 对Vue实例混入beforeCreate钩子操作 和 destroyed 操作
  // 初始化Vue实例的时候都会调用， 也就是 `new Vue` 时会调用
  
  // 当调用 Vue.use(Router) 时，会给全局的 beforeCreate，destroyed 混入2个钩子，
  // 使得在组件初始化时能够通过 this.$router / this.$route 访问到根实例的 router / route 对象
  Vue.mixin({
    beforeCreate () {
      /**
       * this.$options：Vue 实例的初始化选项，new Vue 传递进去的参数
       */
      // 验证vue是否有router对象了，如果有，就不再初始化了（该对象只在根组件上有）
      if (isDef(this.$options.router)) {
        // 没有router对象，会执行以下操作
        // 将_routerRoot指向根组件
        this._routerRoot = this
        // 将router对象挂载到根组件元素_router上
        this._router = this.$options.router
        // 初始化，建立路由监控，这里调用的是 router 的 init 函数，在index.js 文件中
        this._router.init(this)

        // 这里是Vue.util对象，包含一些Vue的工具类方法
        // 这里调用 数据劫持核心方法，代码在shared/util.js
        // 劫持数据_route，一旦_route数据发生变化后，通知router-view执行render方法
        // 也就是 双向绑定
        Vue.util.defineReactive(this, '_route', this._router.history.current)
        // 我们在Vue中打印代码 console.log(this._router === this.$router) 结果是 true
      } else {
        this._routerRoot = (this.$parent && this.$parent._routerRoot) || this
      }
      registerInstance(this, this)
    },
    destroyed () {
      // 销毁实例 销毁实例
      registerInstance(this)
    }
  })
  // router
  Object.defineProperty(Vue.prototype, '$router', {
    get () { return this._routerRoot._router }
  })
  // $route
  Object.defineProperty(Vue.prototype, '$route', {
    get () { return this._routerRoot._route }
  })

  Vue.component('RouterView', View)
  Vue.component('RouterLink', Link)

  // optionMergeStrategies：合并策略
  // 要用于 mixin 以及 Vue.extend() 方法时对于子组件和父组件如果有相同的属性(option)时的合并策略
  const strats = Vue.config.optionMergeStrategies
  // use the same hook merging strategy for route hooks
  strats.beforeRouteEnter = strats.beforeRouteLeave = strats.beforeRouteUpdate = strats.created
}
